/*
* This file is part of Project SkyFire https://www.projectskyfire.org.
* See LICENSE.md file for Copyright information
*/

#ifndef SF_OBJECT_H
#define SF_OBJECT_H

#include "Common.h"
#include "GridReference.h"
#include "Map.h"
#include "ObjectDefines.h"
#include "UpdateMask.h"

#include <set>
#include <sstream>
#include <string>

#define CONTACT_DISTANCE            0.5f
#define INTERACTION_DISTANCE        5.0f
#define ATTACK_DISTANCE             5.0f
#define MAX_VISIBILITY_DISTANCE     SIZE_OF_GRIDS           // max distance for visible objects
#define SIGHT_RANGE_UNIT            50.0f
#define DEFAULT_VISIBILITY_DISTANCE 90.0f                   // default visible distance, 90 yards on continents
#define DEFAULT_VISIBILITY_INSTANCE 170.0f                  // default visible distance in instances, 170 yards
#define DEFAULT_VISIBILITY_BGARENAS 533.0f                  // default visible distance in BG/Arenas, roughly 533 yards

#define DEFAULT_WORLD_OBJECT_SIZE   0.388999998569489f      // player size, also currently used (correctly?) for any non Unit world objects
#define DEFAULT_COMBAT_REACH        1.5f
#define MIN_MELEE_REACH             2.0f
#define NOMINAL_MELEE_RANGE         5.0f
#define MELEE_RANGE                 (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players

enum TypeMask
{
    TYPEMASK_OBJECT = 0x0001,
    TYPEMASK_ITEM = 0x0002,
    TYPEMASK_CONTAINER = 0x0006,                       // TYPEMASK_ITEM | 0x0004
    TYPEMASK_UNIT = 0x0008,                       // creature
    TYPEMASK_PLAYER = 0x0010,
    TYPEMASK_GAMEOBJECT = 0x0020,
    TYPEMASK_DYNAMICOBJECT = 0x0040,
    TYPEMASK_CORPSE = 0x0080,
    TYPEMASK_AREATRIGGER = 0x0100,
    TYPEMASK_SEER = TYPEMASK_PLAYER | TYPEMASK_UNIT | TYPEMASK_DYNAMICOBJECT
};

enum class TypeID
{
    TYPEID_OBJECT = 0,
    TYPEID_ITEM = 1,
    TYPEID_CONTAINER = 2,
    TYPEID_UNIT = 3,
    TYPEID_PLAYER = 4,
    TYPEID_GAMEOBJECT = 5,
    TYPEID_DYNAMICOBJECT = 6,
    TYPEID_CORPSE = 7,
    TYPEID_AREATRIGGER = 8,
    TYPEID_SCENEOBJECT = 9,
    NUM_CLIENT_OBJECT_TYPES = 10
};

TypeID GuidHigh2TypeId(uint32 guid_hi);

enum class TempSummonType
{
    TEMPSUMMON_TIMED_OR_DEAD_DESPAWN = 1,             // despawns after a specified time OR when the creature disappears
    TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN = 2,             // despawns after a specified time OR when the creature dies
    TEMPSUMMON_TIMED_DESPAWN = 3,             // despawns after a specified time
    TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT = 4,             // despawns after a specified time after the creature is out of combat
    TEMPSUMMON_CORPSE_DESPAWN = 5,             // despawns instantly after death
    TEMPSUMMON_CORPSE_TIMED_DESPAWN = 6,             // despawns after a specified time after death
    TEMPSUMMON_DEAD_DESPAWN = 7,             // despawns when the creature disappears
    TEMPSUMMON_MANUAL_DESPAWN = 8              // despawns when UnSummon() is called
};

enum PhaseMasks
{
    PHASEMASK_NORMAL = 0x00000001,
    PHASEMASK_ANYWHERE = 0xFFFFFFFF
};

enum NotifyFlags
{
    NOTIFY_NONE = 0x00,
    NOTIFY_AI_RELOCATION = 0x01,
    NOTIFY_VISIBILITY_CHANGED = 0x02,
    NOTIFY_ALL = 0xFF
};

class Corpse;
class Creature;
class CreatureAI;
class DynamicObject;
class GameObject;
class InstanceScript;
class Player;
class TempSummon;
class Transport;
class Unit;
class UpdateData;
class WorldObject;
class WorldPacket;
class ZoneScript;

typedef UNORDERED_MAP<Player*, UpdateData> UpdateDataMapType;

class Object
{
public:
    virtual ~Object();

    bool IsInWorld() const { return m_inWorld; }

    virtual void AddToWorld();
    virtual void RemoveFromWorld();

    uint64 GetGUID() const { return GetUInt64Value(0); }
    uint32 GetGUIDLow() const { return GUID_LOPART(GetUInt64Value(0)); }
    uint32 GetGUIDMid() const { return GUID_ENPART(GetUInt64Value(0)); }
    uint32 GetGUIDHigh() const { return GUID_HIPART(GetUInt64Value(0)); }
    ObjectGuid GetObjectGUID() const { return static_cast<ObjectGuid>(GetGUID()); }
    const ByteBuffer& GetPackGUID() const { return m_PackGUID; }
    uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY_ID); }
    void SetEntry(uint32 entry) { SetUInt32Value(OBJECT_FIELD_ENTRY_ID, entry); }

    float GetObjectScale() const { return GetFloatValue(OBJECT_FIELD_SCALE); }
    virtual void SetObjectScale(float scale) { SetFloatValue(OBJECT_FIELD_SCALE, scale); }

    TypeID GetTypeId() const { return m_objectTypeId; }
    bool isType(uint16 mask) const { return (mask & m_objectType); }

    virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const;
    void SendUpdateToPlayer(Player* player);

    void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player* target) const;
    void BuildOutOfRangeUpdateBlock(UpdateData* data) const;

    virtual void DestroyForPlayer(Player* target, bool onDeath = false) const;
    void SendDestroyObject(Player* target, ObjectGuid Guid, bool onDeath = false) const;

    int32 GetInt32Value(uint16 index) const;
    uint32 GetUInt32Value(uint16 index) const;
    uint64 GetUInt64Value(uint16 index) const;
    float GetFloatValue(uint16 index) const;
    uint8 GetByteValue(uint16 index, uint8 offset) const;
    uint16 GetUInt16Value(uint16 index, uint8 offset) const;
    uint32 GetDynamicUInt32Value(uint32 tab, uint16 index) const;

    void SetInt32Value(uint16 index, int32 value);
    void SetUInt32Value(uint16 index, uint32 value);
    void UpdateUInt32Value(uint16 index, uint32 value);
    void SetUInt64Value(uint16 index, uint64 value);
    void SetFloatValue(uint16 index, float value);
    void SetByteValue(uint16 index, uint8 offset, uint8 value);
    void SetUInt16Value(uint16 index, uint8 offset, uint16 value);
    void SetInt16Value(uint16 index, uint8 offset, int16 value) { SetUInt16Value(index, offset, (uint16)value); }
    void SetStatFloatValue(uint16 index, float value);
    void SetStatInt32Value(uint16 index, int32 value);
    void SetDynamicUInt32Value(uint32 tab, uint16 index, uint32 value);

    bool AddUInt64Value(uint16 index, uint64 value);
    bool RemoveUInt64Value(uint16 index, uint64 value);

    void ApplyModUInt32Value(uint16 index, int32 val, bool apply);
    void ApplyModInt32Value(uint16 index, int32 val, bool apply);
    void ApplyModPositiveFloatValue(uint16 index, float val, bool apply);
    void ApplyModSignedFloatValue(uint16 index, float val, bool apply);
    void ApplyPercentModFloatValue(uint16 index, float val, bool apply);

    void SetFlag(uint16 index, uint32 newFlag);
    void RemoveFlag(uint16 index, uint32 oldFlag);
    void ToggleFlag(uint16 index, uint32 flag);
    bool HasFlag(uint16 index, uint32 flag) const;
    void ApplyModFlag(uint16 index, uint32 flag, bool apply);

    void SetByteFlag(uint16 index, uint8 offset, uint8 newFlag);
    void RemoveByteFlag(uint16 index, uint8 offset, uint8 newFlag);
    void ToggleByteFlag(uint16 index, uint8 offset, uint8 flag);
    bool HasByteFlag(uint16 index, uint8 offset, uint8 flag) const;

    void SetFlag64(uint16 index, uint64 newFlag);
    void RemoveFlag64(uint16 index, uint64 oldFlag);
    void ToggleFlag64(uint16 index, uint64 flag);
    bool HasFlag64(uint16 index, uint64 flag) const;
    void ApplyModFlag64(uint16 index, uint64 flag, bool apply);

    void ClearUpdateMask(bool remove);

    uint16 GetValuesCount() const { return m_valuesCount; }

    virtual bool hasQuest(uint32 /* quest_id */) const { return false; }
    virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; }
    virtual void BuildUpdate(UpdateDataMapType&) { }
    void BuildFieldsUpdate(Player*, UpdateDataMapType&) const;

    void SetFieldNotifyFlag(uint16 flag) { m_fieldNotifyFlags |= flag; }
    void RemoveFieldNotifyFlag(uint16 flag) { m_fieldNotifyFlags &= ~flag; }

    // FG: some hacky helpers
    void ForceValuesUpdateAtIndex(uint32);

    Player* ToPlayer() { if (GetTypeId() == TypeID::TYPEID_PLAYER) return reinterpret_cast<Player*>(this); else return NULL; }
    Player const* ToPlayer() const { if (GetTypeId() == TypeID::TYPEID_PLAYER) return reinterpret_cast<Player const*>(this); else return NULL; }

    Creature* ToCreature() { if (GetTypeId() == TypeID::TYPEID_UNIT) return reinterpret_cast<Creature*>(this); else return NULL; }
    Creature const* ToCreature() const { if (GetTypeId() == TypeID::TYPEID_UNIT) return reinterpret_cast<Creature const*>(this); else return NULL; }

    Unit* ToUnit() { if (isType(TYPEMASK_UNIT)) return reinterpret_cast<Unit*>(this); else return NULL; }
    Unit const* ToUnit() const { if (isType(TYPEMASK_UNIT)) return reinterpret_cast<Unit const*>(this); else return NULL; }

    GameObject* ToGameObject() { if (GetTypeId() == TypeID::TYPEID_GAMEOBJECT) return reinterpret_cast<GameObject*>(this); else return NULL; }
    GameObject const* ToGameObject() const { if (GetTypeId() == TypeID::TYPEID_GAMEOBJECT) return reinterpret_cast<GameObject const*>(this); else return NULL; }

    Corpse* ToCorpse() { if (GetTypeId() == TypeID::TYPEID_CORPSE) return reinterpret_cast<Corpse*>(this); else return NULL; }
    Corpse const* ToCorpse() const { if (GetTypeId() == TypeID::TYPEID_CORPSE) return reinterpret_cast<Corpse const*>(this); else return NULL; }

    DynamicObject* ToDynObject() { if (GetTypeId() == TypeID::TYPEID_DYNAMICOBJECT) return reinterpret_cast<DynamicObject*>(this); else return NULL; }
    DynamicObject const* ToDynObject() const { if (GetTypeId() == TypeID::TYPEID_DYNAMICOBJECT) return reinterpret_cast<DynamicObject const*>(this); else return NULL; }

    AreaTrigger* ToAreaTrigger() { if (GetTypeId() == TypeID::TYPEID_AREATRIGGER) return reinterpret_cast<AreaTrigger*>(this); else return NULL; }
    AreaTrigger const* ToAreaTrigger() const { if (GetTypeId() == TypeID::TYPEID_AREATRIGGER) return reinterpret_cast<AreaTrigger const*>(this); else return NULL; }

protected:
    Object();

    void _InitValues();
    void _Create(uint32 guidlow, uint32 entry, HighGuid guidhigh);
    std::string _ConcatFields(uint16 startIndex, uint16 size) const;
    void _LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count);

    uint32 GetUpdateFieldData(Player const* target, uint32*& flags) const;

    void BuildMovementUpdate(ByteBuffer* data, uint16 flags) const;
    void BuildDynamicValuesUpdate(ByteBuffer* data) const;
    virtual void BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, Player* target) const;

    std::vector<uint32*> m_dynamicTab;
    std::vector<bool*> m_dynamicChange;

    uint16 m_objectType;

    TypeID m_objectTypeId;
    uint16 m_updateFlag;

    union
    {
        int32* m_int32Values;
        uint32* m_uint32Values;
        float* m_floatValues;
    };

    UpdateMask _changesMask;

    uint16 m_valuesCount;

    uint16 m_fieldNotifyFlags;

    bool m_objectUpdated;

private:
    bool m_inWorld;

    ByteBuffer m_PackGUID;

    // for output helpfull error messages from asserts
    bool PrintIndexError(uint32 index, bool set) const;
    Object(Object const& right) = delete;
    Object& operator=(Object const& right) = delete;
};

struct Position
{
    struct PositionXYZStreamer
    {
        explicit PositionXYZStreamer(Position& pos) : m_pos(&pos) { }
        Position* m_pos;
    };

    struct PositionXYZOStreamer
    {
        explicit PositionXYZOStreamer(Position& pos) : m_pos(&pos) { }
        Position* m_pos;
    };

    float m_positionX;
    float m_positionY;
    float m_positionZ;
    // Better to limit access to m_orientation field, but this will be hard to achieve with many scripts using array initialization for this structure
    //private:
    float m_orientation;
    //public:

    bool operator==(Position const& a);

    inline bool operator!=(Position const& a)
    {
        return !(operator==(a));
    }

    void Relocate(float x, float y)
    {
        m_positionX = x; m_positionY = y;
    }
    void Relocate(float x, float y, float z)
    {
        m_positionX = x; m_positionY = y; m_positionZ = z;
    }
    void Relocate(float x, float y, float z, float orientation)
    {
        m_positionX = x; m_positionY = y; m_positionZ = z; SetOrientation(orientation);
    }
    void Relocate(Position const& pos)
    {
        m_positionX = pos.m_positionX; m_positionY = pos.m_positionY; m_positionZ = pos.m_positionZ; SetOrientation(pos.m_orientation);
    }
    void Relocate(Position const* pos)
    {
        m_positionX = pos->m_positionX; m_positionY = pos->m_positionY; m_positionZ = pos->m_positionZ; SetOrientation(pos->m_orientation);
    }
    void RelocateOffset(Position const& offset);
    void SetOrientation(float orientation)
    {
        m_orientation = NormalizeOrientation(orientation);
    }

    float GetPositionX() const { return m_positionX; }
    float GetPositionY() const { return m_positionY; }
    float GetPositionZ() const { return m_positionZ; }
    float GetOrientation() const { return m_orientation; }

    void GetPosition(float& x, float& y) const
    {
        x = m_positionX; y = m_positionY;
    }
    void GetPosition(float& x, float& y, float& z) const
    {
        x = m_positionX; y = m_positionY; z = m_positionZ;
    }
    void GetPosition(float& x, float& y, float& z, float& o) const
    {
        x = m_positionX; y = m_positionY; z = m_positionZ; o = m_orientation;
    }
    void GetPosition(Position* pos) const
    {
        if (pos)
            pos->Relocate(m_positionX, m_positionY, m_positionZ, m_orientation);
    }

    Position::PositionXYZStreamer PositionXYZStream()
    {
        return PositionXYZStreamer(*this);
    }
    Position::PositionXYZOStreamer PositionXYZOStream()
    {
        return PositionXYZOStreamer(*this);
    }

    bool IsPositionValid() const;

    float GetExactDist2dSq(float x, float y) const
    {
        float dx = m_positionX - x; float dy = m_positionY - y; return dx * dx + dy * dy;
    }
    float GetExactDist2d(const float x, const float y) const
    {
        return sqrt(GetExactDist2dSq(x, y));
    }
    float GetExactDist2dSq(Position const* pos) const
    {
        float dx = m_positionX - pos->m_positionX; float dy = m_positionY - pos->m_positionY; return dx * dx + dy * dy;
    }
    float GetExactDist2d(Position const* pos) const
    {
        return sqrt(GetExactDist2dSq(pos));
    }
    float GetExactDistSq(float x, float y, float z) const
    {
        float dz = m_positionZ - z; return GetExactDist2dSq(x, y) + dz * dz;
    }
    float GetExactDist(float x, float y, float z) const
    {
        return sqrt(GetExactDistSq(x, y, z));
    }
    float GetExactDistSq(Position const* pos) const
    {
        float dx = m_positionX - pos->m_positionX; float dy = m_positionY - pos->m_positionY; float dz = m_positionZ - pos->m_positionZ; return dx * dx + dy * dy + dz * dz;
    }
    float GetExactDist(Position const* pos) const
    {
        return sqrt(GetExactDistSq(pos));
    }

    void GetPositionOffsetTo(Position const& endPos, Position& retOffset) const;

    float GetAngle(Position const* pos) const;
    float GetAngle(float x, float y) const;
    float GetRelativeAngle(Position const* pos) const
    {
        return GetAngle(pos) - m_orientation;
    }
    float GetRelativeAngle(float x, float y) const { return GetAngle(x, y) - m_orientation; }
    void GetSinCos(float x, float y, float& vsin, float& vcos) const;

    bool IsInDist2d(float x, float y, float dist) const
    {
        return GetExactDist2dSq(x, y) < dist * dist;
    }
    bool IsInDist2d(Position const* pos, float dist) const
    {
        return GetExactDist2dSq(pos) < dist * dist;
    }
    bool IsInDist(float x, float y, float z, float dist) const
    {
        return GetExactDistSq(x, y, z) < dist * dist;
    }
    bool IsInDist(Position const* pos, float dist) const
    {
        return GetExactDistSq(pos) < dist * dist;
    }
    bool HasInArc(float arcangle, Position const* pos, float border = 2.0f) const;
    bool HasInLine(WorldObject const* target, float width) const;
    std::string ToString() const;

    // modulos a radian orientation to the range of 0..2PI
    static float NormalizeOrientation(float o)
    {
        // fmod only supports positive numbers. Thus we have
        // to emulate negative numbers
        if (o < 0)
        {
            float mod = o * -1;
            mod = fmod(mod, 2.0f * static_cast<float>(M_PI));
            mod = -mod + 2.0f * static_cast<float>(M_PI);
            return mod;
        }
        return fmod(o, 2.0f * static_cast<float>(M_PI));
    }
};
ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer);
ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer);
ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer);
ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer);

struct MovementInfo
{
    // common
    uint64 guid;
    uint32 flags;
    uint16 flags2;
    Position pos;
    uint32 time;

    // transport
    struct TransportInfo
    {
        void Reset()
        {
            guid = 0;
            pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
            seat = -1;
            time = 0;
            time2 = 0;
            time3 = 0;
        }

        uint64 guid;
        Position pos;
        int8 seat;
        uint32 time;
        uint32 time2;
        uint32 time3;
    } transport;

    // swimming/flying
    float pitch;

    // jumping
    struct JumpInfo
    {
        void Reset()
        {
            fallTime = 0;
            zspeed = sinAngle = cosAngle = xyspeed = 0.0f;
        }

        uint32 fallTime;

        float zspeed, sinAngle, cosAngle, xyspeed;
    } jump;

    // spline
    float splineElevation;

    MovementInfo() :
        guid(0), flags(0), flags2(0), time(0), pitch(0.0f), splineElevation(0.0f)
    {
        pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
        transport.Reset();
        jump.Reset();
    }

    uint32 GetMovementFlags() const { return flags; }
    void SetMovementFlags(uint32 flag) { flags = flag; }
    void AddMovementFlag(uint32 flag) { flags |= flag; }
    void RemoveMovementFlag(uint32 flag) { flags &= ~flag; }
    bool HasMovementFlag(uint32 flag) const { return flags & flag; }

    uint16 GetExtraMovementFlags() const { return flags2; }
    void SetExtraMovementFlags(uint16 flag) { flags2 = flag; }
    void AddExtraMovementFlag(uint16 flag) { flags2 |= flag; }
    void RemoveExtraMovementFlag(uint16 flag) { flags2 &= ~flag; }
    bool HasExtraMovementFlag(uint16 flag) const { return flags2 & flag; }

    void SetFallTime(uint32 time) { jump.fallTime = time; }

    void ResetTransport()
    {
        transport.Reset();
    }

    void ResetJump()
    {
        jump.Reset();
    }

    void OutDebug();
};

#define MAPID_INVALID 0xFFFFFFFF

class WorldLocation : public Position
{
public:
    explicit WorldLocation(uint32 _mapid = MAPID_INVALID, float _x = 0, float _y = 0, float _z = 0, float _o = 0)
        : m_mapId(_mapid) {
        Relocate(_x, _y, _z, _o);
    }
    WorldLocation(const WorldLocation& loc) { WorldRelocate(loc); }

    void WorldRelocate(const WorldLocation& loc)
    {
        m_mapId = loc.GetMapId(); Relocate(loc);
    }
    uint32 GetMapId() const { return m_mapId; }

    uint32 m_mapId;
};

template<class T>
class GridObject
{
public:
    virtual ~GridObject() { }
    bool IsInGrid() const { return _gridRef.isValid(); }
    void AddToGrid(GridRefManager<T>& m) { ASSERT(!IsInGrid()); _gridRef.link(&m, (T*)this); }
    void RemoveFromGrid() { ASSERT(IsInGrid()); _gridRef.unlink(); }
private:
    GridReference<T> _gridRef;
};

template <class T_VALUES, class T_FLAGS, class FLAG_TYPE, uint8 ARRAY_SIZE>
class FlaggedValuesArray32
{
public:
    FlaggedValuesArray32()
    {
        memset(&m_values, 0x00, sizeof(T_VALUES) * ARRAY_SIZE);
        m_flags = 0;
    }

    T_FLAGS  GetFlags() const { return m_flags; }
    bool     HasFlag(FLAG_TYPE flag) const { return m_flags & (1 << flag); }
    void     AddFlag(FLAG_TYPE flag) { m_flags |= (1 << flag); }
    void     DelFlag(FLAG_TYPE flag) { m_flags &= ~(1 << flag); }

    T_VALUES GetValue(FLAG_TYPE flag) const { return m_values[flag]; }
    void     SetValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] = value; }
    void     AddValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] += value; }

private:
    T_VALUES m_values[ARRAY_SIZE];
    T_FLAGS m_flags;
};

enum class MapObjectCellMoveState
{
    MAP_OBJECT_CELL_MOVE_NONE, //not in move list
    MAP_OBJECT_CELL_MOVE_ACTIVE, //in move list
    MAP_OBJECT_CELL_MOVE_INACTIVE, //in move list but should not move
};

class MapObject
{
    friend class Map; //map for moving creatures
    friend class ObjectGridLoader; //grid loader for loading creatures

protected:
    MapObject() : _moveState(MapObjectCellMoveState::MAP_OBJECT_CELL_MOVE_NONE) { _newPosition.Relocate(0.0f, 0.0f, 0.0f, 0.0f); }

private:
    Cell _currentCell;
    Cell const& GetCurrentCell() const { return _currentCell; }
    void SetCurrentCell(Cell const& cell) { _currentCell = cell; }

    MapObjectCellMoveState _moveState;
    Position _newPosition;
    void SetNewCellPosition(float x, float y, float z, float o)
    {
        _moveState = MapObjectCellMoveState::MAP_OBJECT_CELL_MOVE_ACTIVE;
        _newPosition.Relocate(x, y, z, o);
    }
};

class WorldObject : public Object, public WorldLocation
{
protected:
    explicit WorldObject(bool isWorldObject); //note: here it means if it is in grid object list or world object list
public:
    virtual ~WorldObject();

    virtual void Update(uint32 /*time_diff*/) { }

    void _Create(uint32 guidlow, HighGuid guidhigh, uint32 phaseMask);
    virtual void RemoveFromWorld();

    void GetNearPoint2D(float& x, float& y, float distance, float absAngle) const;
    void GetNearPoint(WorldObject const* searcher, float& x, float& y, float& z, float searcher_size, float distance2d, float absAngle) const;
    void GetClosePoint(float& x, float& y, float& z, float size, float distance2d = 0, float angle = 0) const;
    void MovePosition(Position& pos, float dist, float angle);
    void GetNearPosition(Position& pos, float dist, float angle);
    void MovePositionToFirstCollision(Position& pos, float dist, float angle);
    void GetFirstCollisionPosition(Position& pos, float dist, float angle);
    void GetRandomNearPosition(Position& pos, float radius);
    void GetContactPoint(WorldObject const* obj, float& x, float& y, float& z, float distance2d = CONTACT_DISTANCE) const;

    float GetObjectSize() const;
    void UpdateGroundPositionZ(float x, float y, float& z) const;
    void UpdateAllowedPositionZ(float x, float y, float& z) const;

    void GetRandomPoint(Position const& srcPos, float distance, float& rand_x, float& rand_y, float& rand_z) const;
    void GetRandomPoint(Position const& srcPos, float distance, Position& pos) const;

    uint32 GetInstanceId() const { return m_InstanceId; }

    virtual void SetPhaseMask(uint32 newPhaseMask, bool update);
    virtual bool SetPhased(uint32 id, bool update, bool apply);
    bool HasPhaseList(uint32 phase);
    uint32 GetPhaseMask() const { return m_phaseMask; }
    void ClearPhases(bool update = false);
    bool IsPhased(WorldObject const* obj) const;
    bool IsPhased(uint32 phase) const { return _phases.find(phase) != _phases.end(); }
    std::set<uint32> const& GetPhases() const { return _phases; }
    bool IsTerrainSwaped(uint32 terrainSwap) const { return _terrainSwaps.find(terrainSwap) != _terrainSwaps.end(); }
    std::set<uint32> const& GetTerrainSwaps() const { return _terrainSwaps; }
    std::set<uint32> const& GetWorldMapSwaps() const { return _worldMapSwaps; }
    void RebuildTerrainSwaps();
    void RebuildWorldMapAreaSwaps();
    void UpdateAreaPhase();

    bool InSamePhase(WorldObject const* obj) const;
    bool InSamePhase(uint32 phasemask) const { return (GetPhaseMask() & phasemask); }

    uint32 GetZoneId() const;
    uint32 GetAreaId() const;
    void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const;

    InstanceScript* GetInstanceScript();

    std::string const& GetName() const { return m_name; }
    void SetName(std::string const& newname) { m_name = newname; }

    virtual std::string const& GetNameForLocaleIdx(LocaleConstant /*locale_idx*/) const { return m_name; }

    float GetDistance(WorldObject const* obj) const;
    float GetDistance(Position const& pos) const;
    float GetDistance(float x, float y, float z) const;
    float GetDistance2d(WorldObject const* obj) const;
    float GetDistance2d(float x, float y) const;
    float GetDistanceZ(WorldObject const* obj) const;

    bool IsSelfOrInSameMap(WorldObject const* obj) const;
    bool IsInMap(WorldObject const* obj) const;
    bool IsWithinDist3d(float x, float y, float z, float dist) const;
    bool IsWithinDist3d(Position const* pos, float dist) const;
    bool IsWithinDist2d(float x, float y, float dist) const;
    bool IsWithinDist2d(Position const* pos, float dist) const;
    // use only if you will sure about placing both object at same map
    bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D = true) const;
    bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true) const;
    bool IsWithinLOS(float x, float y, float z) const;
    bool IsWithinLOSInMap(WorldObject const* obj) const;
    bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const;
    bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const;
    bool IsInRange2d(float x, float y, float minRange, float maxRange) const;
    bool IsInRange3d(float x, float y, float z, float minRange, float maxRange) const;
    bool isInFront(WorldObject const* target, float arc = M_PI) const;
    bool isInBack(WorldObject const* target, float arc = M_PI) const;

    bool IsInBetween(WorldObject const* obj1, WorldObject const* obj2, float size = 0) const;

    virtual void CleanupsBeforeDelete(bool finalCleanup = true);  // used in destructor or explicitly before mass creature delete to remove cross-references to already deleted units

    virtual void SendMessageToSet(WorldPacket* data, bool self);
    virtual void SendMessageToSetInRange(WorldPacket* data, float dist, bool self);
    virtual void SendMessageToSet(WorldPacket* data, Player const* skipped_rcvr);

    virtual uint8 getLevelForTarget(WorldObject const* /*target*/) const { return 1; }

    void MonsterSay(const char* text, Language language, WorldObject const* target);
    void MonsterYell(const char* text, Language language, WorldObject const* target);
    void MonsterTextEmote(const char* text, WorldObject const* target, bool IsBossEmote = false);
    void MonsterWhisper(const char* text, Player const* target, bool IsBossWhisper = false);
    void MonsterSay(int32 textId, Language language, WorldObject const* target);
    void MonsterYell(int32 textId, Language language, WorldObject const* target);
    void MonsterTextEmote(int32 textId, WorldObject const* target, bool IsBossEmote = false);
    void MonsterWhisper(int32 textId, Player const* target, bool IsBossWhisper = false);

    void PlayDistanceSound(uint32 sound_id, Player* target);
    void PlayDirectSound(uint32 sound_id, Player* target = NULL);

    void SendObjectDeSpawnAnim(uint64 guid);

    virtual void SaveRespawnTime() { }
    void AddObjectToRemoveList();

    float GetGridActivationRange() const;
    float GetVisibilityRange() const;
    float GetSightRange(WorldObject const* target = NULL) const;
    bool CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false, bool distanceCheck = false) const;

    FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealth;
    FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealthDetect;

    FlaggedValuesArray32<int32, uint32, InvisibilityType, TOTAL_INVISIBILITY_TYPES> m_invisibility;
    FlaggedValuesArray32<int32, uint32, InvisibilityType, TOTAL_INVISIBILITY_TYPES> m_invisibilityDetect;

    FlaggedValuesArray32<int32, uint32, ServerSideVisibilityType, TOTAL_SERVERSIDE_VISIBILITY_TYPES> m_serverSideVisibility;
    FlaggedValuesArray32<int32, uint32, ServerSideVisibilityType, TOTAL_SERVERSIDE_VISIBILITY_TYPES> m_serverSideVisibilityDetect;

    // Low Level Packets
    void SendPlaySound(uint32 Sound, bool OnlySelf);

    virtual void SetMap(Map* map);
    virtual void ResetMap();
    Map* GetMap() const { ASSERT(m_currMap); return m_currMap; }
    Map* FindMap() const { return m_currMap; }
    //used to check all object's GetMap() calls when object is not in world!

    //this function should be removed in nearest time...
    Map const* GetBaseMap() const;

    void SetZoneScript();
    ZoneScript* GetZoneScript() const { return m_zoneScript; }

    TempSummon* SummonCreature(uint32 id, Position const& pos, TempSummonType spwtype = TempSummonType::TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0) const;
    TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TempSummonType::TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0) const;
    GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime);
    Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, CreatureAI* (*GetAI)(Creature*) = NULL);
    void SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list = NULL);

    Creature* FindNearestCreature(uint32 entry, float range, bool alive = true) const;
    GameObject* FindNearestGameObject(uint32 entry, float range) const;
    GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const;

    void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange) const;
    void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange) const;
    void GetPlayerListInGrid(std::list<Player*>& lList, float fMaxSearchRange) const;

    void DestroyForNearbyPlayers();
    virtual void UpdateObjectVisibility(bool forced = true);
    void BuildUpdate(UpdateDataMapType&);

    //relocation and visibility system functions
    void AddToNotify(uint16 f) { m_notifyflags |= f; }
    bool isNeedNotify(uint16 f) const { return m_notifyflags & f; }
    uint16 GetNotifyFlags() const { return m_notifyflags; }
    bool NotifyExecuted(uint16 f) const { return m_executed_notifies & f; }
    void SetNotified(uint16 f) { m_executed_notifies |= f; }
    void ResetAllNotifies() { m_notifyflags = 0; m_executed_notifies = 0; }

    bool isActiveObject() const { return m_isActive; }
    void setActive(bool isActiveObject);
    void SetWorldObject(bool apply);
    bool IsPermanentWorldObject() const { return m_isWorldObject; }
    bool IsWorldObject() const;

    template<class NOTIFIER> void VisitNearbyObject(float const& radius, NOTIFIER& notifier) const { if (IsInWorld()) GetMap()->VisitAll(GetPositionX(), GetPositionY(), radius, notifier); }
    template<class NOTIFIER> void VisitNearbyGridObject(float const& radius, NOTIFIER& notifier) const { if (IsInWorld()) GetMap()->VisitGrid(GetPositionX(), GetPositionY(), radius, notifier); }
    template<class NOTIFIER> void VisitNearbyWorldObject(float const& radius, NOTIFIER& notifier) const { if (IsInWorld()) GetMap()->VisitWorld(GetPositionX(), GetPositionY(), radius, notifier); }

#ifdef MAP_BASED_RAND_GEN
    int32 irand(int32 min, int32 max) const { return int32(GetMap()->mtRand.randInt(max - min)) + min; }
    uint32 urand(uint32 min, uint32 max) const { return GetMap()->mtRand.randInt(max - min) + min; }
    int32 rand32() const { return GetMap()->mtRand.randInt(); }
    double rand_norm() const { return GetMap()->mtRand.randExc(); }
    double rand_chance() const { return GetMap()->mtRand.randExc(100.0); }
#endif

    uint32  LastUsedScriptID;

    // Transports
    Transport* GetTransport() const { return m_transport; }
    float GetTransOffsetX() const { return m_movementInfo.transport.pos.GetPositionX(); }
    float GetTransOffsetY() const { return m_movementInfo.transport.pos.GetPositionY(); }
    float GetTransOffsetZ() const { return m_movementInfo.transport.pos.GetPositionZ(); }
    float GetTransOffsetO() const { return m_movementInfo.transport.pos.GetOrientation(); }
    uint32 GetTransTime()   const { return m_movementInfo.transport.time; }
    int8 GetTransSeat()     const { return m_movementInfo.transport.seat; }
    virtual uint64 GetTransGUID()   const;
    void SetTransport(Transport* t) { m_transport = t; }

    MovementInfo m_movementInfo;

    virtual float GetStationaryX() const { return GetPositionX(); }
    virtual float GetStationaryY() const { return GetPositionY(); }
    virtual float GetStationaryZ() const { return GetPositionZ(); }
    virtual float GetStationaryO() const { return GetOrientation(); }

protected:
    std::string m_name;
    bool m_isActive;
    const bool m_isWorldObject;
    ZoneScript* m_zoneScript;

    // transports
    Transport* m_transport;

    //these functions are used mostly for Relocate() and Corpse/Player specific stuff...
    //use them ONLY in LoadFromDB()/Create() funcs and nowhere else!
    //mapId/instanceId should be set in SetMap() function!
    void SetLocationMapId(uint32 _mapId) { m_mapId = _mapId; }
    void SetLocationInstanceId(uint32 _instanceId) { m_InstanceId = _instanceId; }

    virtual bool IsNeverVisible() const { return !IsInWorld(); }
    virtual bool IsAlwaysVisibleFor(WorldObject const* /*seer*/) const { return false; }
    virtual bool IsInvisibleDueToDespawn() const { return false; }
    //difference from IsAlwaysVisibleFor: 1. after distance check; 2. use owner or charmer as seer
    virtual bool IsAlwaysDetectableFor(WorldObject const* /*seer*/) const { return false; }
private:
    Map* m_currMap;                                    //current object's Map location

    //uint32 m_mapId;                                     // object at map with map_id
    uint32 m_InstanceId;                                // in map copy with instance id
    uint32 m_phaseMask;                                 // in area phase state
    std::set<uint32> _phases;
    std::set<uint32> _terrainSwaps;
    std::set<uint32> _worldMapSwaps;

    uint16 m_notifyflags;
    uint16 m_executed_notifies;
    virtual bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D) const;

    bool CanNeverSee(WorldObject const* obj) const;
    virtual bool CanAlwaysSee(WorldObject const* /*obj*/) const { return false; }
    bool CanDetect(WorldObject const* obj, bool ignoreStealth) const;
    bool CanDetectInvisibilityOf(WorldObject const* obj) const;
    bool CanDetectStealthOf(WorldObject const* obj) const;
};

namespace Skyfire
{
    // Binary predicate to sort WorldObjects based on the distance to a reference WorldObject
    class ObjectDistanceOrderPred
    {
    public:
        ObjectDistanceOrderPred(WorldObject const* pRefObj, bool ascending = true) : m_refObj(pRefObj), m_ascending(ascending) { }
        bool operator()(WorldObject const* pLeft, WorldObject const* pRight) const
        {
            return m_ascending ? m_refObj->GetDistanceOrder(pLeft, pRight) : !m_refObj->GetDistanceOrder(pLeft, pRight);
        }
    private:
        WorldObject const* m_refObj;
        const bool m_ascending;
    };
}

#endif
